

package 
{	
	import away3d.cameras.lenses.PerspectiveLens;	
	import away3d.core.managers.Stage3DManager;
	import away3d.core.managers.Stage3DProxy;
	import away3d.core.pick.PickingColliderType;
	import away3d.debug.AwayStats;
	import away3d.events.MouseEvent3D;
	import away3d.events.Scene3DEvent;		
	import away3d.events.Stage3DEvent;
	import flash.desktop.NativeApplication;	
	import flash.display.DisplayObjectContainer;	
	import flash.text.AntiAliasType;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import kizzo.particle_system.ParticleSystem;
	import org.papervision3d.core.proto.DisplayObjectContainer3D;
	
	import flash.display.Sprite;	
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	import flash.events.TouchEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;	
	import flash.geom.Point;	
	import flash.system.Capabilities;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	import kizzo.game.*;
	import kizzo.game.tile_objects.*;
	import kizzo.geometry.TileCoord3D;
	
	import kizzo.gui.EditObject;
	import kizzo.gui.LabelledTextInput;
	import kizzo.gui.PropertyMenu;
	
	import org.flashdevelop.utils.FlashConnect;
	import starling.core.Starling;			
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	
	import flash.geom.Vector3D;	
	import away3d.containers.*;	
	import away3d.entities.*;
	import away3d.materials.*;
	import away3d.primitives.*;
	import away3d.utils.*;	
	
	
		
	public class Main extends Sprite 	
	{					
		
		
		private static var instance:Main;
			
		public var state_array:Vector.<Vector.<Vector.<TileState>>>;		
		public var object_list:Vector.<TileObject>;
		
		public var master_cube:GameCube;
		private var slave_cube_a:GameCube;
		private var slave_cube_b:GameCube;
		private var cubes:Vector.<GameCube>;
		
		private var game_state:GameState;		
		
		public var view_3D:View3D;	
		
		private var stage_3D_manager:Stage3DManager;
		private var stage_3D_proxy:Stage3DProxy;
		private var parallax_bg:Starling;
		
		public var mouse_picker_plane:Mesh;
		
		private var unlinked_teleport_tile:TeleportTile;	
		
		private var CAMERA_ORIGIN:Vector3D;
		
		public var activator_sets:Vector.<ActivatorSet>;		
				
		private var input_controller:InputController;
				
		public var level_list:Vector.<String> = new < String > [	"level_1.xml", "level_2.xml", "level_3.xml", "level_4.xml", "level_5.xml", "level_6.xml", "level_7.xml", "level_8.xml",
																	"level_9.xml", "level_10.xml", "level_11.xml", "level_12.xml", "level_13.xml", "level_14.xml", "level_15.xml", "level_16.xml", "level_17.xml"];
		public var current_level:int;
		private var STARTING_LEVEL:int = 0;
		
		private var hud:HUD;
		
		
		[SWF(width="854", height="480", frameRate="60")]
		
		
		
		public function Main():void 
		{						
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;			
			
			instance = this;
			
			this.stage.addEventListener(Event.DEACTIVATE, deactivate);			
			
			this.object_list = new Vector.<TileObject>();
			
			init_state_array();
			
			if (stage) 
				init();
			else 
				addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		
		private function init(e:Event = null):void 
		{						
			this.removeEventListener(Event.ADDED_TO_STAGE, init);						
			
			this.stage_3D_manager = Stage3DManager.getInstance(this.stage);
			this.stage_3D_proxy = stage_3D_manager.getFreeStage3DProxy();
			this.stage_3D_proxy.antiAlias = 4;
			this.stage_3D_proxy.color = 0x0;
			
			this.stage_3D_proxy.addEventListener(Stage3DEvent.CONTEXT3D_CREATED, on_context_created);
		}	
		
		
		
		private function on_context_created(e:Stage3DEvent):void
		{			
			TileCoord3D.init();
			MaterialLib.init();
			GeometryLib.init();		
			SoundLib.init();			
			
			this.view_3D = new View3D();	
			this.view_3D.stage3DProxy = stage_3D_proxy;
			this.view_3D.shareContext = true;			
						
			this.view_3D.antiAlias = 4;
			this.view_3D.width = 854;
			this.view_3D.height = 480;
			
			//setup the camera
			CAMERA_ORIGIN = new Vector3D( -800, 1500, 800);			
			view_3D.camera.lens = new PerspectiveLens(14);				
			
			// mouse picking plane for editor
			this.mouse_picker_plane = new Mesh(new PlaneGeometry(1000, 1000), MaterialLib.GRID_MATERIAL);
			this.mouse_picker_plane.pickingCollider = PickingColliderType.AS3_FIRST_ENCOUNTERED; 
			this.mouse_picker_plane.mouseEnabled = true;
			view_3D.scene.addChild(this.mouse_picker_plane);
			
			addChild(view_3D);			
			//addChild(new AwayStats(view_3D));				
			
			parallax_bg = new Starling(StarlingSprite, stage, stage_3D_proxy.viewPort, stage_3D_proxy.stage3D);					
						
			this.game_state = new GameState();
			
			this.stage_3D_proxy.addEventListener(Event.ENTER_FRAME, frame_handler);
			
			this.input_controller = InputController.get_instance();
			this.input_controller.enable_edit_input();			
			this.hud = new HUD();
			addChild(hud);
			
			this.current_level = STARTING_LEVEL;
			build_level(load_state_xml(level_list[this.current_level]));
		}		
		
		
		
		
		private function frame_handler(e:Event):void
		{			
			parallax_bg.nextFrame();
			view_3D.render();
		}						
		
		
		
		private function init_state_array():void
		{
			state_array = new Vector.<Vector.<Vector.<TileState>>>(Game.ARRAY_SIZE);
			
				for (var x_i:uint = 0; x_i < Game.ARRAY_SIZE; x_i++)
				{
					state_array[x_i] = new Vector.<Vector.<TileState>>(Game.ARRAY_SIZE);
					for (var y_i:uint = 0; y_i < Game.ARRAY_SIZE; y_i++)
					{		
						state_array[x_i][y_i] = new Vector.<TileState>(Game.ARRAY_SIZE);
						for (var z_i:uint = 0; z_i < Game.ARRAY_SIZE; z_i++)
						{
							state_array[x_i][y_i][z_i] = new TileState();
						}
					}
				}
		}
		
		
		
		public function shift_state_array(from:int, to:int, step:int, v:TileCoord3D):void
		{		
			for (var x_i:uint = from; x_i != to; x_i+=step)
			{				
				for (var y_i:uint = from; y_i != to; y_i+=step)
				{							
					for (var z_i:uint = from; z_i != to; z_i+=step)
					{
						state_array[x_i][y_i][z_i] = state_array[x_i + v.x][y_i + v.y][z_i + v.z];
					}
				}
			}
		}		
		
		
		
		public function state_array_to_xml(state_array:Vector.<Vector.<Vector.<TileState>>>):XML
		{
			var state_array_xml:XML = <state/>;
			
			for (var x_i:uint = 0; x_i < Game.ARRAY_SIZE; x_i++)
			{								
				for (var y_i:uint = 0; y_i < Game.ARRAY_SIZE; y_i++)
				{						
					for (var z_i:uint = 0; z_i < Game.ARRAY_SIZE; z_i++)
					{
						var state:TileState = state_array[x_i][y_i][z_i];
						
						if (state.value != 0)
						state_array_xml.appendChild(tile_state_to_xml(state, new TileCoord3D(x_i-Game.ARRAY_OFFSET, y_i-Game.ARRAY_OFFSET, z_i-Game.ARRAY_OFFSET)));
					}
				}
			}			
			return state_array_xml;
		}
		
		
		
		private function tile_state_to_xml(state:TileState, p:TileCoord3D):XML
		{
				var tile_state_xml:XML = <tile/>;
				
				tile_state_xml.@x = p.x;
				tile_state_xml.@y = p.y;
				tile_state_xml.@z = p.z;
				tile_state_xml.@val = state.value;				
				
				if (state.has_flag(TileState.TELEPORT_TILE))
				{
					var teleport:TeleportTile = state.get_object_ref_by_type(TileState.TELEPORT_TILE) as TeleportTile;								
					var teleport_xml:XML = <teleport/>;
					teleport_xml.@x = teleport.link.x;
					teleport_xml.@y = teleport.link.y;
					teleport_xml.@z = teleport.link.z;								
					tile_state_xml.appendChild(teleport_xml);
				}
				
				if (state.has_flag(TileState.ACTIVATED))
				{
					var activated:ActivatedTile = state.get_object_ref_by_type(TileState.ACTIVATED) as ActivatedTile;
					var activated_xml:XML = <activated/>;
					activated_xml.@id = activated.id;					
					tile_state_xml.appendChild(activated_xml);
				}
				
				if (state.has_flag(TileState.ACTIVATOR))
				{
					var activator:ActivatorTile = state.get_object_ref_by_type(TileState.ACTIVATOR) as ActivatorTile;
					var activator_xml:XML = <activator/>;
					activator_xml.@id = activator.id;
					activator_xml.@delay = activator_sets[activator.id].delay;
					tile_state_xml.appendChild(activator_xml);								
				}													

			return tile_state_xml;
		}
		
		
		
		public function save_state_xml():void
		{
			var state_xml:XML = state_array_to_xml(this.state_array);
						
			//var f:File = File.applicationStorageDirectory.resolvePath("level_16.xml");
			var f:File = File.applicationStorageDirectory.resolvePath(this.level_list[this.current_level]);
			var fs:FileStream = new FileStream();
			
			fs.open(f,FileMode.WRITE);
			fs.writeUTFBytes(state_xml.toString());
			fs.close();  			
		}
		
		
		
		public function load_state_xml(level_name:String):XML
		{						
			//var f:File = File.applicationStorageDirectory.resolvePath("level_16.xml");
			var f:File = File.applicationStorageDirectory.resolvePath(level_name);
			//var f:File = File.applicationDirectory.resolvePath(level_name);					
			
			var fs:FileStream = new FileStream();
			fs.open(f, FileMode.READ);			
			
			var xml_string:String;
			xml_string = fs.readMultiByte(fs.bytesAvailable, "utf-8");
			fs.close();
			
			return new XML(xml_string);			
		}		
		
		
		
		public function build_level(level_xml:XML):void
		{	
			var x:uint, y:uint, z:uint, val:uint;
			
			// clean existing level
			for (var i:uint = 0; i < this.object_list.length; i++)
			{	
				if (object_list[i].mesh.parent != null)					
					view_3D.scene.removeChild(object_list[i].mesh);
			}			
			
			this.hud.set_level(this.current_level + 1);
			
			this.object_list = new Vector.<TileObject>();			
			init_state_array();	
			this.cubes = new Vector.<GameCube>();
			this.input_controller.flush_input_buffer();
			
			activator_sets = new Vector.<ActivatorSet>(20);
			
			for (i = 0; i < 20; i++)
				activator_sets[i] = new ActivatorSet();
			
			for each (var node:XML in level_xml..activator)
			{
				activator_sets[int(node.@id)] = new ActivatorSet();				
			}			
			
			this.game_state = new GameState();
			
			for each(node in level_xml.tile)
			{						
				populate_tile(node);
			}
						
			//view_3D.camera.position = CAMERA_ORIGIN;
			//view_3D.camera.lookAt(new Vector3D());
			
			view_3D.camera.position = new Vector3D( 0, 3000, 0);			
			var cam_tweener:CameraTweener = new CameraTweener(view_3D.camera);
			cam_tweener.tween_to(CAMERA_ORIGIN, 20, new Vector3D());			

			input_controller.enable_game_input();
		}
		
		
		
		public function populate_tile(tile_xml:XML):void
		{			
			var p:TileCoord3D = new TileCoord3D(int(tile_xml.@x), int(tile_xml.@y), int(tile_xml.@z));
			var val:uint = uint(tile_xml.@val);			
			
			var new_tile_state:TileState = new TileState(val);
			var tile_state:TileState = state_array[p.x_o][p.y_o][p.z_o];			
			
			var mesh:Mesh;			
			var tile_object:TileObject;			
			
			if (new_tile_state.value == TileState.EMPTY)
			{				
				depopulate_tile(p);
			}
			
			if (new_tile_state.has_flag(TileState.SIMPLE_TILE))
			{	
				if (!tile_state.has_tile())
				{															
					mesh = new Mesh(GeometryLib.TILE2, MaterialLib.SIMPLE_TILES[(((p.x_o<<13)*p.z_o)>>5)%5]);
					tile_object = new TileObject(mesh, p, TileState.SIMPLE_TILE);					
										
					this.view_3D.scene.addChild(mesh);					
					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}
			
			
			if (new_tile_state.has_flag(TileState.MASTER_CUBE))
			{
				if (game_state.HAS_MASTER_CUBE == false)
				{
					game_state.HAS_MASTER_CUBE = true;					
					
					mesh = new Mesh(GeometryLib.CUBE, MaterialLib.MASTER_CUBE);
					this.master_cube = new GameCube(mesh, p, TileState.MASTER_CUBE, MaterialLib.MASTER_LIGHT);
					this.master_cube.set_goal(TileState.MASTER_GOAL);
					this.cubes.push(this.master_cube);
					tile_object = this.master_cube;					 
										
					this.view_3D.scene.addChild(mesh);					
					
					mesh.position = new Vector3D(p.x_m, p.y_m + Game.TILE/2 + Game.TILE_HALF_HEIGHT, p.z_m);
					
					tile_state.add_object(master_cube);	
				}
			}
			
			
			if (new_tile_state.has_flag(TileState.MASTER_GOAL))
			{	
				if (!tile_state.has_tile() && game_state.HAS_MASTER_GOAL == false)
				{
					game_state.HAS_MASTER_GOAL = true;					
					
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.MASTER_GOAL);					
					tile_object = new TileObject(mesh, p, TileState.MASTER_GOAL);					
										
					this.view_3D.scene.addChild(mesh);					
					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);
					
					tile_state.add_object(tile_object);
				}	
			}
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_CUBE_A))
			{
				if (game_state.HAS_SLAVE_CUBE_A == false)
				{
					game_state.HAS_SLAVE_CUBE_A = true;					
					
					mesh = new Mesh(GeometryLib.CUBE, MaterialLib.SLAVE_CUBE_A);
					this.slave_cube_a = new GameCube(mesh, p, TileState.SLAVE_CUBE_A, MaterialLib.SLAVE_LIGHT_A);
					this.slave_cube_a.set_goal(TileState.SLAVE_GOAL_A);
					this.cubes.push(this.slave_cube_a);
					tile_object = this.slave_cube_a;
					
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y * Game.TILE + .5 * Game.TILE + Game.TILE_HALF_HEIGHT   , p.z_m);
										
					tile_state.add_object(slave_cube_a);	
				}
			}
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_LINK_A))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.SLAVE_LINK_A);
					tile_object = new TileObject(mesh, p, TileState.SLAVE_LINK_A);					
										
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}	
			}				
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_GOAL_A))
			{	
				if (!tile_state.has_tile() && game_state.HAS_SLAVE_GOAL_A == false)
				{
					game_state.HAS_SLAVE_GOAL_A = true;					
					
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.SLAVE_GOAL_A);
					tile_object = new TileObject(mesh, p, TileState.SLAVE_GOAL_A);					
										
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}	
			}	
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_CUBE_B))
			{
				if (game_state.HAS_SLAVE_CUBE_B == false)
				{
					game_state.HAS_SLAVE_CUBE_B = true;					
					
					mesh = new Mesh(GeometryLib.CUBE, MaterialLib.SLAVE_CUBE_B);
					this.slave_cube_b = new GameCube(mesh, p, TileState.SLAVE_CUBE_B, MaterialLib.SLAVE_LIGHT_B);
					this.slave_cube_b.set_goal(TileState.SLAVE_GOAL_B);
					this.cubes.push(this.slave_cube_b);
					tile_object = this.slave_cube_b;
					
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y * Game.TILE + .5 * Game.TILE + Game.TILE_HALF_HEIGHT, p.z_m);
										
					tile_state.add_object(slave_cube_b);	
				}
			}
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_LINK_B))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.SLAVE_LINK_B);
					tile_object = new TileObject(mesh, p, TileState.SLAVE_LINK_B);					
										
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}	
			}				
			
			
			if (new_tile_state.has_flag(TileState.SLAVE_GOAL_B))
			{	
				if (!tile_state.has_tile() && game_state.HAS_SLAVE_GOAL_B == false)
				{
					game_state.HAS_SLAVE_GOAL_B = true;					
					
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.SLAVE_GOAL_B);
					tile_object = new TileObject(mesh, p, TileState.SLAVE_GOAL_B);					
					
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}	
			}			
			
			
			if (new_tile_state.has_flag(TileState.CRUMBLING_TILE))
			{
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.CRUMBLING_TILE);
					tile_object = new TileObject(mesh, p, TileState.CRUMBLING_TILE);					
										
					this.view_3D.scene.addChild(mesh);					
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}
			}			
			
			
			if (new_tile_state.has_flag(TileState.NEGATOR_TILE))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.NEGATOR_TILE);
					tile_object = new TileObject(mesh, p, TileState.NEGATOR_TILE);					
										
					this.view_3D.scene.addChild(mesh);						
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);		
					
					tile_state.add_object(tile_object);
				}	
			}					
			
			
			if (new_tile_state.has_flag(TileState.ROTATE_CLOCKWISE_MOD))
			{	
				if (!tile_state.has_tile())
				{					
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.ROTATE_CLOCKWISE_MOD);
					tile_object = new TileObject(mesh, p, TileState.ROTATE_CLOCKWISE_MOD);					
										
					this.view_3D.scene.addChild(mesh);						
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);							
					
					tile_state.add_object(tile_object);
				}	
			}	
			
			
			if (new_tile_state.has_flag(TileState.BOOST_MOD))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.BOOST_MOD);
					tile_object = new TileObject(mesh, p, TileState.BOOST_MOD);					
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}				
			
			
			if (new_tile_state.has_flag(TileState.BLOCKER_CUBE))
			{	
				if (!tile_state.has_cube())
				{										
					mesh = new Mesh(GeometryLib.CUBE, MaterialLib.BLOCKER_CUBE);
					tile_object = new TileObject(mesh, p, TileState.BLOCKER_CUBE);					
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y * Game.TILE + .5 * Game.TILE + Game.TILE_HALF_HEIGHT   , p.z_m);				
					
					tile_state.add_object(tile_object);
				}	
			}			
			
			
			if (new_tile_state.has_flag(TileState.TELEPORT_TILE))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.TELEPORT_TILE);
					var teleport_tile:TeleportTile = new TeleportTile(mesh, p, TileState.TELEPORT_TILE);	
					
					var teleport_xml:XML = tile_xml.teleport[0];
					teleport_tile.link = new TileCoord3D(int(teleport_xml.@x), int(teleport_xml.@y), int(teleport_xml.@z));					
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(teleport_tile);
				}	
			}				
			
			
			if (new_tile_state.has_flag(TileState.MIRROR_Z))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.MIRROR_Z);
					tile_object = new TileObject(mesh, p, TileState.MIRROR_Z);					
										
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}						
			
			
			if (new_tile_state.has_flag(TileState.MIRROR_X))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.MIRROR_Z);
					tile_object = new TileObject(mesh, p, TileState.MIRROR_X);					
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}					
			
			
			if (new_tile_state.has_flag(TileState.ACTIVATOR))
			{
				if (!tile_state.has_tile())
				{										
					var activator_xml:XML = tile_xml.activator[0];
					var id:int = int(activator_xml.@id);
					var delay:Number = Number(activator_xml.@delay);					
					
					mesh = new Mesh(GeometryLib.TILE2, MaterialLib.ACTIVATOR_OFF);
					tile_object = new ActivatorTile(mesh, p, TileState.ACTIVATOR, id);	
					
					activator_sets[id].add_activator(tile_object as ActivatorTile);
					activator_sets[id].delay = delay;
					
					this.view_3D.scene.addChild(mesh);
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);					
				}
			}	
			
			
			if (new_tile_state.has_flag(TileState.ACTIVATED))
			{	
				if (!tile_state.has_tile())
				{										
					var activated_xml:XML = tile_xml.activated[0];
					id = int(activated_xml.@id);
					
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.UNACTIVATED);
					tile_object = new ActivatedTile(mesh, p, TileState.ACTIVATED, id);
					
					activator_sets[id].addEventListener(ActivatorSet.ACTIVATED, ActivatedTile(tile_object).activate);
					activator_sets[id].addEventListener(ActivatorSet.DEACTIVATED, ActivatedTile(tile_object).deactivate);
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}				
			

			if (new_tile_state.has_flag(TileState.REPULSOR))
			{	
				if (!tile_state.has_tile())
				{										
					mesh = new Mesh(GeometryLib.TILE, MaterialLib.REPULSOR);
					tile_object = new TileObject(mesh, p, TileState.REPULSOR);					
					
					this.view_3D.scene.addChild(mesh);							
					mesh.position = new Vector3D(p.x_m, p.y_m, p.z_m);					
					
					tile_state.add_object(tile_object);
				}	
			}		
			
		}
		
		
		
		private function depopulate_tile(p:TileCoord3D):void
		{
			var tile_state:TileState = TileState(state_array[p.x_o][p.y_o][p.z_o]);
			
			
			if (tile_state.has_flag(TileState.MASTER_CUBE))				
				game_state.HAS_MASTER_CUBE = false;
			
			if (tile_state.has_flag(TileState.MASTER_GOAL))
				game_state.HAS_MASTER_GOAL = false;
			
			if (tile_state.has_flag(TileState.SLAVE_CUBE_A))
				game_state.HAS_SLAVE_CUBE_A = false;
			
			if (tile_state.has_flag(TileState.SLAVE_GOAL_A))			
				game_state.HAS_SLAVE_GOAL_A = false;
			
			if (tile_state.has_flag(TileState.SLAVE_LINK_A))			
				game_state.HAS_SLAVE_LINK_A = false;
				
			if (tile_state.has_flag(TileState.SLAVE_CUBE_B))
				game_state.HAS_SLAVE_CUBE_B = false;
			
			if (tile_state.has_flag(TileState.SLAVE_GOAL_B))			
				game_state.HAS_SLAVE_GOAL_B = false;
			
			if (tile_state.has_flag(TileState.SLAVE_LINK_B))			
				game_state.HAS_SLAVE_LINK_B = false;				
						
			var objects:Vector.<TileObject> = tile_state.get_objects();
			
			while (objects.length > 0)
			{
				var obj:TileObject = objects.pop();				
				view_3D.scene.removeChild(obj.mesh);
			}		
			
			tile_state.clear_objects();			
			tile_state.reset();
		}	
		
		
		
		public function start_move(v:TileCoord3D):void
		{	
			this.hud.increment_moves();
			move_cube(this.master_cube, v);
		}
		
		
		private function move_cube(cube:GameCube, v:TileCoord3D):void
		{
			
			var start_position:TileCoord3D = cube.tile_coord;
			var start_tile_state:TileState = state_array[start_position.x_o][start_position.y_o][start_position.z_o];			
			
			var target_position:TileCoord3D = cube.tile_coord.add(v);
			var target_tile_state:TileState = state_array[target_position.x_o][target_position.y_o][target_position.z_o];			
			
			if (!target_tile_state.has_cube())
			{
				cube.addEventListener(GameCube.ROLL_COMPLETE, roll_complete_hnd);
				cube.roll(v);				
			}
			else
			{
				input_controller.enable_game_input();
			}
		}			
		
		
		
		private function roll_complete_hnd(e:Event):void
		{				
			var cube:GameCube = e.target as GameCube;
			var move_complete:Boolean = true; // moves by all cubes are complete?
			 
			cube.removeEventListener(GameCube.ROLL_COMPLETE, roll_complete_hnd);
			
			var start_position:TileCoord3D = cube.tile_coord;
			var start_tile_state:TileState = state_array[start_position.x_o][start_position.y_o][start_position.z_o];						
			start_tile_state.tile_dispatch_cube_event(new Event(TileEvent.CUBE_OFF));	
			
			
			var target_position:TileCoord3D = cube.roll_target;
			var target_tile_state:TileState = state_array[target_position.x_o][target_position.y_o][target_position.z_o];						
			target_tile_state.tile_dispatch_cube_event(new Event(TileEvent.CUBE_ON));		
			
			
			start_tile_state.clear_flag(cube.type);
			cube.tile_coord = cube.roll_target;
			target_tile_state.set_flag(cube.type);
			
			var fall:Boolean = false;			
			
			if (!target_tile_state.has_tile())
			{						
				fall = true;
			}				
			if (target_tile_state.has_flag(TileState.ACTIVATED))
			{
				var activated_tile:ActivatedTile = target_tile_state.get_object_ref_by_type(TileState.ACTIVATED) as ActivatedTile;
				if (!activated_tile.is_active)
					fall = true;
			}
			
			if (fall)
			{
				if 	(cube == master_cube ||
					(cube == slave_cube_a && game_state.HAS_SLAVE_GOAL_A) ||
					(cube == slave_cube_b && game_state.HAS_SLAVE_GOAL_B))
					{
						cube.fall();
						cube.addEventListener(GameCube.FALL_COMPLETE, fall_complete_hnd);			
						move_complete = false;				
					}
				else
				{
						cube.fall();
						cube.addEventListener(GameCube.FALL_COMPLETE, non_lethal_fall_complete_hnd);			
				}
			}			
			else
			{
				if (start_tile_state.has_flag(TileState.CRUMBLING_TILE))
				{
					var crumbling_tile:TileObject = start_tile_state.get_object_by_type(TileState.CRUMBLING_TILE);
					MaterialLib.CRUMBLING_TILE_ALPHA.alpha = MaterialLib.CRUMBLING_TILE.alpha;
					crumbling_tile.mesh.material = MaterialLib.CRUMBLING_TILE_ALPHA;
					crumbling_tile.tween_alpha(0);
					crumbling_tile.addEventListener(TileObject.TWEEN_ALPHA_COMPLETE, crumble_alpha_tween_hnd);					
				}
				
				
				if (!(start_tile_state.has_flag(TileState.NEGATOR_TILE) && target_tile_state.has_flag(TileState.NEGATOR_TILE)))				
				{
					for (var i:uint; i < cube.links.length; i++)
					{						
						var link:Link = Link(cube.links[i]);
						if (link.boost == true)
						{							
							link.link_child.boost_complete = false;
						}
						move_complete = false;
						move_cube(link.link_child, link.modify(cube.tile_vector));						
					}					
				}		
				
				
				if (target_tile_state.has_flag(TileState.ROTATE_CLOCKWISE_MOD))
				{
					for (i = 0; i < cube.links.length; i++)
					{
						var rot_mod:RotationModifier = cube.links[i].get_modifier(RotationModifier) as RotationModifier;
						if (rot_mod == null)
						{
							rot_mod = new RotationModifier();							
							cube.links[i].add_modifier(rot_mod);
						}						
						rot_mod.increment(); // increments in 90 degrees
					}
				}
				
				
				if (target_tile_state.has_flag(TileState.MIRROR_Z))
				{	
					for (i = 0; i < cube.links.length; i++)
					{
						if (cube.links[i].mirror_z == false)
						{
							cube.add_modifier(new MirrorModifier(new TileCoord3D(1, 1, -1)));
							cube.links[i].mirror_z == true;
						}
					}										
				}						
				
				
				if (target_tile_state.has_flag(TileState.MIRROR_X))
				{
					for (i = 0; i < cube.links.length; i++)
					{
						if (cube.links[i].mirror_x == false)
						{
							cube.add_modifier(new MirrorModifier(new TileCoord3D(-1, 1, 1)));
							cube.links[i].mirror_x == true;
						}
					}						
				}								
				
				
				if (target_tile_state.has_flag(TileState.TELEPORT_TILE))
				{
					var teleport:TeleportTile = target_tile_state.get_object_ref_by_type(TileState.TELEPORT_TILE) as TeleportTile;					
					target_tile_state.clear_flag(cube.type);
					var teleport_state:TileState = state_array[teleport.link.x_o][teleport.link.y_o][teleport.link.z_o];
					teleport_state.set_flag(cube.type);					
					cube.teleport(teleport.link);
				}	
				
				
				/*if (target_tile_state.has_flag(TileState.ACTIVATOR))
				{
					var activator:ActivatorTile = target_tile_state.get_object_ref_by_type(TileState.ACTIVATOR) as ActivatorTile;
					//activator.dispatchEvent(new Event(TileEvent.CUBE_ON));					
				}
				
				
				if (start_tile_state.has_flag(TileState.ACTIVATOR))
				{
					var activator_tile:TileObject = start_tile_state.get_object_ref_by_type(TileState.ACTIVATOR);
					//ActivatorTile(activator_tile).dispatchEvent(new Event(TileEvent.CUBE_OFF));
				}*/
				
				
				if (target_tile_state.has_flag(TileState.REPULSOR))
				{
					if (cube == master_cube)
						cube.repulsor = true;					
				}				
				
				
				if (cube.repulsor == true)
				{
					for (i = 0; i < this.cubes.length; i++)
					{
						if (cube != this.cubes[i])
						{
							var distance:Vector3D = cube.mesh.position.subtract(cubes[i].mesh.position);
							if (distance.length < Game.REPULSE_DISTANCE)
							{
								if (Math.abs(distance.z) > Math.abs(distance.x))
								{
									if (distance.z > 0)
									{
										move_cube(cubes[i], TileCoord3D.NE_VECTOR);
									}
									else
									{
										move_cube(cubes[i], TileCoord3D.SW_VECTOR);
									}
								}
								else
								{
									if (distance.x > 0)
									{
										move_cube(cubes[i], TileCoord3D.SE_VECTOR);
									}
									else
									{
										move_cube(cubes[i], TileCoord3D.NW_VECTOR);
									}
								}
							}							
						}
					}
				}
				
				
				if (cube.boost_complete == false)
				{										
					cube.boost_complete = true;
					move_complete = false;
					move_cube(cube, cube.tile_vector);					
				}		
				
				
				if (target_tile_state.has_flag(TileState.BOOST_MOD))
				{					
					for (i = 0; i < cube.links.length; i++)
					{						
						//cube.links[i].add_modifier(new BoostModifier());
						cube.links[i].boost = true;
					}
				}	
				
				if (target_tile_state.has_flag(TileState.SLAVE_LINK_A))
				{
					if (cube != this.slave_cube_a)
					{
						if (!cube.is_linked(slave_cube_a))
						{
							FlashConnect.trace("link slave a");
							cube.add_link(slave_cube_a);							
						}
					}
				}
				
				if (target_tile_state.has_flag(TileState.SLAVE_LINK_B))
				{
					if (cube != this.slave_cube_b)
					{
						if (!cube.is_linked(slave_cube_b))
						{
							FlashConnect.trace("link slave b");
							cube.add_link(slave_cube_b);							
						}
					}
				}				
			}	
			
			
			if (move_complete)
			{								
				dispatchEvent(new Event(TileEvent.MOVE_COMPLETE));
				
				var goal_complete:Boolean = true;
				
				for (i = 0; i < cubes.length; i++)
				{
					var pos:TileCoord3D = cubes[i].tile_coord;
					var state:TileState = state_array[pos.x_o][pos.y_o][pos.z_o];
					if (!state.has_flag(cubes[i].get_goal()))
						goal_complete = false;
				}
				
				if (goal_complete)
				{
					//Soundlib.
					this.current_level = (this.current_level+1)%this.level_list.length;
					build_level(load_state_xml(level_list[this.current_level]));
					//FlashConnect.trace("WIN");
				}
				
				this.input_controller.enable_game_input();					
			}
		}
		
		
		
		// once crumbling tile alpha is 0 - remove it
		private function crumble_alpha_tween_hnd(e:Event):void
		{
			var crumbling_tile:TileObject = e.target as TileObject;			
			view_3D.scene.removeChild(crumbling_tile.mesh);
			
			var coords:TileCoord3D = crumbling_tile.tile_coord;
			var tile_state:TileState = state_array[coords.x_o][coords.y_o][coords.z_o];
			tile_state.clear_flag(TileState.CRUMBLING_TILE);			
		}
		
		
		
		// cube fall complete - reset level
		private function fall_complete_hnd(e:Event):void
		{		
			SoundLib.FALL.play();
			view_3D.camera.position = CAMERA_ORIGIN;
			view_3D.camera.lookAt(new Vector3D());			
			build_level(load_state_xml(this.level_list[this.current_level]));
		}
		

		
		private function non_lethal_fall_complete_hnd(e:Event):void
		{	
			var cube:GameCube = e.target as GameCube;	
			var p:TileCoord3D = cube.tile_coord;
			var state:TileState = state_array[p.x_o][p.y_o][p.z_o];						
			
			this.cubes.splice(this.cubes.indexOf(cube), 1);			
			state.clear_flag(cube.type);			
			view_3D.scene.removeChild(cube.mesh);			
			
			var incoming_link:Link = cube.incoming_link;
			incoming_link.link_parent.remove_link(incoming_link);			
		}		
		
		
		
		private function deactivate(e:Event):void 
		{
			// auto-close
			NativeApplication.nativeApplication.exit();
		}		
		
		
		
		// for Starling
		public static function get_instance():Main
		{
			return instance;
		}
		
	}	
}